package com.dragon.auth.config;

import com.dragon.auth.support.CustomeOAuth2AccessTokenGenerator;
import com.dragon.auth.support.core.CustomeOAuth2TokenCustomizer;
import com.dragon.auth.support.core.DragonDaoAuthenticationProvider;
import com.dragon.auth.support.core.FormIdentityLoginConfigurer;
import com.dragon.auth.support.filter.PasswordDecoderFilter;
import com.dragon.auth.support.handler.DragonAuthenticationFailureEventHandler;
import com.dragon.auth.support.handler.DragonAuthenticationSuccessEventHandler;
import com.dragon.auth.support.password.OAuth2ResourceOwnerPasswordAuthenticationConverter;
import com.dragon.auth.support.password.OAuth2ResourceOwnerPasswordAuthenticationProvider;
import com.dragon.common.core.constant.SecurityConstants;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2RefreshTokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.security.oauth2.server.authorization.web.authentication.*;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.util.Arrays;

/**
 * @date: 2024/8/6 11:03
 * @author: ybl
 */
@Configuration
@RequiredArgsConstructor
public class AuthorizationServerConfiguration {

    private final OAuth2AuthorizationService authorizationService;
    private final PasswordDecoderFilter passwordDecoderFilter;

    /**
     * OAuth2 授权服务器的安全过滤链
     * @param http
     * @return
     * @throws Exception
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    @ConditionalOnProperty(value = "security.micro", matchIfMissing = true)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {

        // 配置 OAuth2 授权服务器
        OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer();

        // 增加密码解密过滤器
        http.addFilterBefore(passwordDecoderFilter, UsernamePasswordAuthenticationFilter.class);


        // 配置认证授权端点
        authorizationServerConfigurer.tokenEndpoint(tokenEndpoint ->
                tokenEndpoint
                        .accessTokenRequestConverter(accessTokenRequestConverter()) // 自定义授权认证Converter
                        .accessTokenResponseHandler(new DragonAuthenticationSuccessEventHandler()) // 登录成功处理器
                        .errorResponseHandler(new DragonAuthenticationFailureEventHandler()) // 登录失败处理器
        ).clientAuthentication(oAuth2ClientAuthenticationConfigurer ->
                oAuth2ClientAuthenticationConfigurer
                        .errorResponseHandler(new DragonAuthenticationFailureEventHandler()) // 处理客户端认证异常
        ).authorizationEndpoint(authorizationEndpoint ->
                authorizationEndpoint
                        .consentPage(SecurityConstants.CUSTOM_CONSENT_PAGE_URI) // 授权码端点确认页面
        );

        // 创建 DefaultSecurityFilterChain
        DefaultSecurityFilterChain securityFilterChain = http
                .apply(authorizationServerConfigurer)
                .authorizationService(authorizationService) // 使用 Redis 存储 token
                .authorizationServerSettings(
                        AuthorizationServerSettings.builder().issuer(SecurityConstants.PROJECT_LICENSE).build()
                )
                .and()
                .apply(new FormIdentityLoginConfigurer()) // 配置表单登录
                .and()
                .build();

        // 注入自定义授权模式实现
        addCustomOAuth2GrantAuthenticationProvider(http);

        return securityFilterChain;
    }

    /**
     * 请求转换器
     * // TODO: 2024/8/9 完善 
     *
     * @return DelegatingAuthenticationConverter
     */
    @Bean
    public AuthenticationConverter accessTokenRequestConverter() {
        return new DelegatingAuthenticationConverter(Arrays.asList(
                new OAuth2ResourceOwnerPasswordAuthenticationConverter(),
                new OAuth2RefreshTokenAuthenticationConverter(),
                new OAuth2ClientCredentialsAuthenticationConverter(),
                new OAuth2AuthorizationCodeAuthenticationConverter(),
                new OAuth2AuthorizationCodeRequestAuthenticationConverter()
        ));
    }

    /**
     * 注入授权模式实现提供方
     * <p>
     * 1. 密码模式 </br>
     * 2. 短信登录 </br>
     */
    @SuppressWarnings("unchecked")
    private void addCustomOAuth2GrantAuthenticationProvider(HttpSecurity http) {
        AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
        OAuth2AuthorizationService authorizationService = http.getSharedObject(OAuth2AuthorizationService.class);

        OAuth2ResourceOwnerPasswordAuthenticationProvider resourceOwnerPasswordAuthenticationProvider = new OAuth2ResourceOwnerPasswordAuthenticationProvider(
                authenticationManager, authorizationService, oAuth2TokenGenerator());

//        OAuth2ResourceOwnerSmsAuthenticationProvider resourceOwnerSmsAuthenticationProvider = new OAuth2ResourceOwnerSmsAuthenticationProvider(
//                authenticationManager, authorizationService, oAuth2TokenGenerator());

        // 处理 UsernamePasswordAuthenticationToken
        http.authenticationProvider(new DragonDaoAuthenticationProvider());
        // 处理 OAuth2ResourceOwnerPasswordAuthenticationToken
        http.authenticationProvider(resourceOwnerPasswordAuthenticationProvider);
        // 处理 OAuth2ResourceOwnerSmsAuthenticationToken
//        http.authenticationProvider(resourceOwnerSmsAuthenticationProvider);
    }

    /**
     * 令牌生成规则实现 </br>
     * client:username:uuid
     * @return OAuth2TokenGenerator
     */
    @Bean
    public OAuth2TokenGenerator oAuth2TokenGenerator() {
        CustomeOAuth2AccessTokenGenerator accessTokenGenerator = new CustomeOAuth2AccessTokenGenerator();
        // 注入Token 增加关联用户信息
        accessTokenGenerator.setAccessTokenCustomizer(new CustomeOAuth2TokenCustomizer());
        return new DelegatingOAuth2TokenGenerator(accessTokenGenerator, new OAuth2RefreshTokenGenerator());
    }

}